﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using Microsoft.Win32;

namespace Tzync
{
    public partial class TzyncUIForm : Form, IObserver, IDisplay
    {
        private String localFolderPath; //path of folder chosen by user
        private TzyncUIState traceState;    //state UI is currently at
        private Difference difference;    //a difference
        private List<Difference> conflictList;  //list of conflicts
        private List<Difference> nonConflictList;  //list of non-conflicts
        private List<RollbackFile> rollbackList;
        private bool firstSyncA; //1st time sync at A
        private bool firstSyncB;    //1st time sync at B
        private SyncTool syncTool;
        ResolveConflictUIForm resolveForm;

        public TzyncUIForm()
        {
            syncTool = new SyncTool();
            syncTool.RegisterObserver(this);    //UI observes subjects for changes
            syncTool.SetCPUID(Utility.GetCPUId()); // get cpu ID and store in sync tool class
            traceState = TzyncUIState.Started;
            //initializations
            localFolderPath = "";
            difference = null;
            nonConflictList = new List<Difference>();
            conflictList = new List<Difference>();
            InitializeComponent();
        }

        //when form loads
        private void TzyncUI_Load(object sender, EventArgs e)
        {
            //defaults      
            analyzeBtn.Enabled = false;
            syncBtn.Enabled = false;
            //rbBtn.Enabled = false;
            //rbPreviewBtn.Enabled = false;
            browseBtnToolTip.SetToolTip(browseBtn, "Click here to chooose a folder to sync");
            dragPictureBoxToolTip.SetToolTip(selectedFolderPanel, "Drag a folder here to select it for synchronization");
            analyzeBtnToolTip.SetToolTip(analyzeBtn, "Click here to analyze for changes");
            syncBtnToolTip.SetToolTip(syncBtn, "Click here to sync the chosen folder");
            helpBtnToolTip.SetToolTip(helpBtn, "Click here to get online help for Tzync");
            //rollbackBtnToolTip.SetToolTip(rbBtn, "Click here to rollback selected files");
            //previewBtnToolTip.SetToolTip(rbPreviewBtn, "Click here to preview the selected files");
        }
        
        //browse button
        private void browseBtn_Click(object sender, EventArgs e)
        {
            try
            {
                DialogResult browseFolderResult = syncFolderBrowserDialog.ShowDialog();
                if (browseFolderResult == DialogResult.OK)  //if user clicks on OK
                {
                    if (traceState == TzyncUIState.MetaDataLoaded)
                    {
                        reloadProgram();
                        pathTextBox.Text = syncFolderBrowserDialog.SelectedPath;
                        localFolderPath = syncFolderBrowserDialog.SelectedPath;
                        FolderSelected();
                    }
                    else
                    {
                        pathTextBox.Text = syncFolderBrowserDialog.SelectedPath;
                        localFolderPath = syncFolderBrowserDialog.SelectedPath;
                        FolderSelected();
                    }
                }
                else
                    return;
            }
            catch (Exception)
            {
                MessageBox.Show("Ouch that hurts. Don't do this again",
                                        "Attention!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            }
        }

        private void selectedFolderPanel_DragEnter(object sender, DragEventArgs e)
        {
            //only allow files to be dropped here
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true)
                e.Effect = DragDropEffects.All;
        }

        private void selectedFolderPanel_DragDrop(object sender, DragEventArgs e)
        {
            string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
            localFolderPath = files[0];
            pathTextBox.Text = files[0];
            FolderSelected();
        }

        private void FolderSelected()
        {
            try
            {
                selectedFolderPictureBox.Image = selectedFolderPictureBox.InitialImage;
                dragLabel.Text = "Folder Selected";
                VerifySelectedPath();   //verify pathname
                LoadMetaData();
                if (firstSyncA == true)
                    analyzeBtn.Enabled = false;
                else if (firstSyncB == true)
                    analyzeBtn.Enabled = false;
                else
                    analyzeBtn.Enabled = true;
                
                syncBtn.Enabled = true;
            }
            catch (InvalidPathException)
            {
                MessageBox.Show("Please choose a valid folder!",
                                        "Attention!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }

        //verifies if path of selected folder is valid
        private void VerifySelectedPath()
        {
            if (!Directory.Exists(localFolderPath))
                throw new InvalidPathException();
        }

        private void reloadProgram()
        {
            traceState = TzyncUIState.Started;
            localFolderPath = syncTool.GetRootAddress();
            syncTool = new SyncTool();
            FolderSelected();
            syncTool.RegisterObserver(this);
            pathTextBox.Text = localFolderPath;
        }

        //performs some actions when a folder is selected by user
        private void LoadMetaData()
        {
            try
            {
                syncTool.Load(localFolderPath);    //scan for differences, load metadata 
            }
            catch (InvalidRootAddressException)
            {
                MessageBox.Show("An error has occurred while loading data! Please choose the correct sync folder.",
                                "Sorry!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            traceState = TzyncUIState.MetaDataLoaded;

            firstSyncA = syncTool.GetFirstTime();    //find out if it's 1st sync at A
            firstSyncB = syncTool.GetSecondTime();  //find out if it's 1st sync at B
        }

        //analyze only
        private void analyzeBtn_Click(object sender, EventArgs e)
        {
            try
            {
                if (traceState == TzyncUIState.MetaDataLoaded)
                {
                    syncTool.Analyze();    //start analyze
                    traceState = TzyncUIState.Analyzed;
                    resultsLabel.Text = "Results of Analysis";

                    MessageBox.Show("We are done analyzing!",
                                "Hurray!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Ouch that hurts. Don't do this again",
                                        "Attention!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            }
        }

        //sync button
        private void syncBtn_Click_1(object sender, EventArgs e)
        {
            try
            {
                firstSyncA = syncTool.GetFirstTime();    //find out if it's 1st sync at A
                firstSyncB = syncTool.GetSecondTime();  //find out if it's 1st sync at B
                DialogResult syncResult = MessageBox.Show("Are you sure you want to start sychronization?",
                                                "Attention!", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

                if (syncResult == DialogResult.Yes)
                {
                    if (firstSyncA == true)
                    {
                        MessageBox.Show("As this is your first sync, the files here will be considered the newest.",
                                        "Attention!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }

                    else if (firstSyncB == true)
                    {
                        MessageBox.Show("As this is your first sync on this computer, we will " +
                                         "copy the files from your other computer here.",
                                        "Attention!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }

                    else
                    {
                        try
                        {
                            if (traceState == TzyncUIState.Started)
                                return;
                            else if (traceState != TzyncUIState.Analyzed && traceState == TzyncUIState.MetaDataLoaded)    //skip analyze if it has already been done
                                syncTool.Analyze();
                        }
                        catch (InvalidPathException)
                        {
                            MessageBox.Show("Please choose a valid folder!",
                                            "Attention!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                        catch (ArgumentNullException)
                        {
                            MessageBox.Show("Please choose a valid folder!",
                                            "Attention!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                    }

                    if (traceState != TzyncUIState.Synced)
                    {
                        syncTool.Sync();   //start sync
                        traceState = TzyncUIState.Synced;

                        if (firstSyncB == true)
                        {
                            firstSyncB = false;
                            reloadProgram();
                        }

                        if (conflictList.Count > 0)
                        {
                            DialogResult conflictPresent = MessageBox.Show("We are done synchronizing but there are conflicts found! Click OK to resolve them.",
                                    "Almost there!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            if (conflictPresent == DialogResult.OK)
                            {
                                resolveForm = new ResolveConflictUIForm(syncTool, conflictList);
                                resolveForm.ShowDialog();

                                DisplayResults();

                            }

                        }
                        else
                        {
                            resultsLabel.Text = "Results of Synchronization";
                            MessageBox.Show("We are done synchronizing!",
                                    "Hurray!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        }
                    }
                    else
                        return;
                }
                else if (syncResult == DialogResult.No)
                    return;
            }
            catch (Exception)
            {
                MessageBox.Show("Ouch that hurts. Don't do this again",
                                        "Attention!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            }
        }

        private void DisplayResults()
        {
            List<Difference> resolvedDifferenceList = syncTool.GetResolvedDifferenceList();
            ListViewItem resultItems;
            TreeNode tNode = new TreeNode();
            bool toAdd = false;

            Type typeOfDifference;   //file or folder
            Pair<DirectionType, OperationType> differenceDetails;

            foreach (Difference resolvedDifference in resolvedDifferenceList)
            {

                resultItems = new ListViewItem();
                typeOfDifference = resolvedDifference.GetItemType();
                differenceDetails = resolvedDifference.GetPairType();
                toAdd = DisplayNonConflictResults(resultItems, typeOfDifference, differenceDetails, tNode, resolvedDifference);  //display it

                if (toAdd == true)
                    diffListView.Items.Add(resultItems);  //display differences
            }
            //foreach (Difference nonConflict in nonConflictList)
            //{

            //    resultItems = new ListViewItem();
            //    typeOfDifference = nonConflict.GetItemType();
            //    differenceDetails = nonConflict.GetPairType();
            //    toAdd = DisplayNonConflictResults(resultItems, typeOfDifference, differenceDetails, tNode, nonConflict);  //display it

            //    if (toAdd == true)
            //        diffListView.Items.Add(resultItems);  //display differences
            //}

            resultsLabel.Text = "Results of Synchronization";
        }

        private bool DisplayNonConflictResults(ListViewItem i, Type typeOfDifference, Pair<DirectionType, OperationType> differenceDetails, TreeNode tNode, Difference d)
        {
            //displays appropriate type of operation
            if (typeOfDifference == Type.File)
            {
                FileInfo fileInfo = d.GetSysFile();
                tNode = d.GetPartnerFile();
                if (syncTool.GetSystemName() == SystemType.Host)
                {
                    i.Text = tNode.getFullPathA(); //path of file/folder  
                    i.SubItems.Add("" + tNode.getSyncA());  //last sync time of file/folder
                }
                else if (syncTool.GetSystemName() == SystemType.Guest)
                {
                    i.Text = tNode.getFullPathB(); //path of file/folder  
                    i.SubItems.Add("" + tNode.getSyncB());  //last sync time of file/folder
                }

                if (differenceDetails.Second == OperationType.Creation)
                {
                    i.SubItems.Add("Created file");
                }
                else if (differenceDetails.Second == OperationType.Deletion)
                {
                    i.SubItems.Add("Deleted file");
                }
                else if (differenceDetails.Second == OperationType.Modification)
                {
                    i.SubItems.Add("Modified file");
                }
                else
                {
                    i.SubItems.Add("Resolved conflict");
                }

                //displays appropriate direction of sync
                if (differenceDetails.First == DirectionType.SystemToHD)
                    i.SubItems.Add("To other computer");
                else if (differenceDetails.First == DirectionType.HDToSystem)
                    i.SubItems.Add("To this computer");

                return true;
            }

            else if (typeOfDifference == Type.Folder)
            {
                if (differenceDetails.Second != OperationType.Modification)   //ignore folder modifications
                {
                    DirectoryInfo dirInfo = d.GetSysFolder();
                    tNode = d.GetPartnerFolder();

                    if (syncTool.GetSystemName() == SystemType.Host)
                    {
                        i.Text = tNode.getFullPathA(); //path of file/folder  
                        i.SubItems.Add("" + tNode.getSyncA());  //last sync time of file/folder
                    }
                    else if (syncTool.GetSystemName() == SystemType.Guest)
                    {
                        i.Text = tNode.getFullPathB(); //path of file/folder  
                        i.SubItems.Add("" + tNode.getSyncB());  //last sync time of file/folder
                    }

                    if (difference.GetPairType().Second == OperationType.Creation)
                        i.SubItems.Add("Created folder");
                    else if (difference.GetPairType().Second == OperationType.Deletion)
                        i.SubItems.Add("Deleted folder");
                    else
                        i.SubItems.Add("Resolved conflict");

                    //displays appropriate direction of sync
                    if (difference.GetPairType().First == DirectionType.SystemToHD)
                        i.SubItems.Add("To other computer");
                    else if (difference.GetPairType().First == DirectionType.HDToSystem)
                        i.SubItems.Add("To this computer");

                    return true;
                }
            }

            return false;
        }

        //tells UI to update its display
        public void Update(ISubject subject)
        {
            DifferenceSubject ds = (DifferenceSubject)subject;
            difference = ds.GetDifference();    //a new difference has been found
            Display();  //display it
        }

        //displays content
        public void Display()
        {
            DisplayDifference();
        }

        //displays differences found in listview
        private void DisplayDifference()
        {
            ListViewItem differenceItems = new ListViewItem();
            TreeNode tNode = new TreeNode();
            bool toAdd = false;

            Type typeOfDifference = difference.GetItemType();   //file or folder
            Pair<DirectionType, OperationType> differenceDetails = difference.GetPairType();

            //display conflicts
            if (difference.GetPairType().First == DirectionType.Nil)
                toAdd = DisplayConflict(differenceItems, typeOfDifference, differenceDetails, tNode);

            //display other types of differences
            else
                toAdd = DisplayNonConflict(differenceItems, typeOfDifference, differenceDetails, tNode);

            if (toAdd == true)
            {
                diffListView.Items.Add(differenceItems);  //display differences
                //MessageBox.Show("DETECT DEL " + differenceItems.Text);
            }
        }

        private bool DisplayConflict(ListViewItem items, Type typeOfDifference, Pair<DirectionType, OperationType> differenceDetails, TreeNode tNode)
        {
            
            if (typeOfDifference == Type.File)
            {
                conflictList.Add(difference);
                FileInfo fileInfo = difference.GetSysFile();
                tNode = difference.GetPartnerFile();

                if (syncTool.GetSystemName() == SystemType.Host)
                {
                    items.Text = tNode.getFullPathA(); //path of file/folder  
                    items.SubItems.Add("" + tNode.getSyncA());  //last sync time of file/folder
                }
                else if (syncTool.GetSystemName() == SystemType.Guest)
                {
                    items.Text = tNode.getFullPathB(); //path of file/folder  
                    items.SubItems.Add("" + tNode.getSyncB());  //last sync time of file/folder
                }

                if (differenceDetails.Second == OperationType.CreationCreationConflict)
                {
                    items.SubItems.Add("Conflict - file created on both computers");
                }
                else if (differenceDetails.Second == OperationType.ModDeleteConflict)
                {
                    items.SubItems.Add("Conflict - file modified on 1 computer and deleted on the other");
                }
                else if (differenceDetails.Second == OperationType.ModModConflict)
                {
                    items.SubItems.Add("Conflict - file modified on both computers");
                }
                else if (differenceDetails.Second == OperationType.DeletionDeletionConflict)
                {
                    items.SubItems.Add("Conflict - file deleted on both computers");
                }

                items.SubItems.Add("To be resolved");     //direction of operation
                items.ForeColor = Color.Red;

                return true;
            }

            else if (typeOfDifference == Type.Folder)
            {
                //ignore del-del, mod-mod and creation-creation folder conflicts
                if (differenceDetails.Second != OperationType.DeletionDeletionConflict
                && differenceDetails.Second != OperationType.ModModConflict && differenceDetails.Second != OperationType.CreationCreationConflict)
                {
                    conflictList.Add(difference);
                    DirectoryInfo dirInfo = difference.GetSysFolder();
                    tNode = difference.GetPartnerFolder();

                    if (syncTool.GetSystemName() == SystemType.Host)
                    {
                        items.Text = tNode.getFullPathA(); //path of file/folder  
                        items.SubItems.Add("" + tNode.getSyncA());  //last sync time of file/folder
                    }
                    else if (syncTool.GetSystemName() == SystemType.Guest)
                    {
                        items.Text = tNode.getFullPathB(); //path of file/folder  
                        items.SubItems.Add("" + tNode.getSyncB());  //last sync time of file/folder
                    }

                    if (differenceDetails.Second == OperationType.ModDeleteConflict)
                    {
                        items.SubItems.Add("Conflict - folder modified on 1 computer and deleted on the other");
                    }
                    else if (differenceDetails.Second == OperationType.CreationCreationConflict)
                    {
                        items.SubItems.Add("Conflict - folder created on both computers");
                    }
                    else if (differenceDetails.Second == OperationType.DeletionDeletionConflict)
                    {
                        items.SubItems.Add("Conflict - folder deleted on both computers");
                    }

                    items.SubItems.Add("To be resolved");     //direction of operation
                    items.ForeColor = Color.Red;

                    return true;
                }
            }

            return false;
        }

        private bool DisplayNonConflict(ListViewItem items, Type typeOfDifference, Pair<DirectionType, OperationType> differenceDetails, TreeNode tNode)
        {
            
            //displays appropriate type of operation
            if (typeOfDifference == Type.File)
            {
                nonConflictList.Add(difference);
                FileInfo fileInfo = difference.GetSysFile();
                tNode = difference.GetPartnerFile();

                if (syncTool.GetSystemName() == SystemType.Host)
                {
                    items.Text = tNode.getFullPathA(); //path of file/folder  
                    items.SubItems.Add("" + tNode.getSyncA());  //last sync time of file/folder
                }
                else if (syncTool.GetSystemName() == SystemType.Guest)
                {
                    items.Text = tNode.getFullPathB(); //path of file/folder  
                    items.SubItems.Add("" + tNode.getSyncB());  //last sync time of file/folder
                }

                if (differenceDetails.Second == OperationType.Creation)
                {
                    items.SubItems.Add("Created file");
                }
                else if (differenceDetails.Second == OperationType.Deletion)
                {
                    items.SubItems.Add("Deleted file");
                }
                else if (differenceDetails.Second == OperationType.Modification)
                {
                    items.SubItems.Add("Modified file");
                }
                //displays appropriate direction of sync
                if (differenceDetails.First == DirectionType.SystemToHD)
                {
                    items.SubItems.Add("To other computer");
                }
                else if (differenceDetails.First == DirectionType.HDToSystem)
                {
                    items.SubItems.Add("To this computer");
                }

                return true;
            }

            else if (typeOfDifference == Type.Folder)
            {
                if (differenceDetails.Second != OperationType.Modification)   //ignore folder modifications
                {
                    nonConflictList.Add(difference);
                    DirectoryInfo dirInfo = difference.GetSysFolder();
                    tNode = difference.GetPartnerFolder();

                    if (syncTool.GetSystemName() == SystemType.Host)
                    {
                        items.Text = tNode.getFullPathA(); //path of file/folder  
                        items.SubItems.Add("" + tNode.getSyncA());  //last sync time of file/folder
                    }
                    else if (syncTool.GetSystemName() == SystemType.Guest)
                    {
                        items.Text = tNode.getFullPathB(); //path of file/folder  
                        items.SubItems.Add("" + tNode.getSyncB());  //last sync time of file/folder
                    }

                    if (difference.GetPairType().Second == OperationType.Creation)
                    {
                        items.SubItems.Add("Created folder");
                    }
                    else if (difference.GetPairType().Second == OperationType.Deletion)
                    {
                        items.SubItems.Add("Deleted folder");
                    }

                    //displays appropriate direction of sync
                    if (difference.GetPairType().First == DirectionType.SystemToHD)
                    {
                        items.SubItems.Add("To other computer");
                    }
                    else if (difference.GetPairType().First == DirectionType.HDToSystem)
                    {
                        items.SubItems.Add("To this computer");
                    }

                    return true;
                }
            }

            return false;
        }

        //private void mainTabControl_SelectedIndexChanged(object sender, EventArgs e)
        //{
        //    if (mainTabControl.SelectedIndex == 1)  //rollback tab
        //        DisplayRollback();
        //    else if (mainTabControl.SelectedIndex == 0)
        //        rbListView.Clear();
        //}

        //private void DisplayRollback()
        //{
        //    ListViewItem rollbackItems = new ListViewItem();
        //    rollbackList = syncTool.readRollMeta();
        //    bool rollbackFileExists = false;

        //    if (rollbackList.Count > 0)
        //    {
        //        foreach (RollbackFile r in rollbackList)
        //        {
        //            if (r.getSys() == syncTool.GetSystemName())
        //            {
        //                rollbackFileExists = true;
        //                rollbackItems.Text = r.getPath();
        //                rollbackItems.SubItems.Add("" + r.getLastSyncTime());
        //                rbListView.Items.Add(rollbackItems);
        //            }
        //        }
        //    }

        //    if (rollbackFileExists)
        //    {
        //        rbBtn.Enabled = true;
        //        rbPreviewBtn.Enabled = true;
        //    }
        //}

        ////rollback preview button
        //private void rbPreviewBtn_Click(object sender, EventArgs e)
        //{
        //    foreach (ListViewItem item in rbListView.CheckedItems)
        //    {
        //        Process.Start(item.Text);
        //    }
        //}

        //private void conflictListView_SelectedIndexChanged(object sender, EventArgs e)
        //{
        //    rbBtn.Enabled = true;
        //}

        //private void rbBtn_Click(object sender, EventArgs e)
        //{
        //    List<string> selectedRollbackList = new List<string>();

        //    foreach (ListViewItem item in rbListView.CheckedItems)
        //    {
        //        selectedRollbackList.Add(item.Text);
        //    }
        //    syncTool.ExecuteRoll(selectedRollbackList);
        //}

        private void helpBtn_Click(object sender, EventArgs e)
        {
            Process.Start(GetDefaultBrowserPath(), "http://code.google.com/p/tzync/wiki/Welcome");
        }

        private static string GetDefaultBrowserPath()
        {
            string key = @"htmlfile\shell\open\command";
            RegistryKey registryKey =
            Registry.ClassesRoot.OpenSubKey(key, false);
            // get default browser path
            return ((string)registryKey.GetValue(null, null)).Split('"')[1];
        }

    }   //end of TzyncUI class
}   
